package Server;

import java.io.IOException;
import java.net.BindException;
import java.net.ServerSocket;
import java.rmi.Naming;
import java.rmi.RemoteException;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;
import java.rmi.server.RemoteRef;
import java.util.Observable;

import rmi.RMI_NavalBattle_Service;
import logic.Log;



public class Server extends Observable{

	public final static int MAX_LOGGED_USERS = 10;
	public final static int SERVICE_PORT = 1234;
	public final static int TIMEOUT = 2000;
	public final static String USERS_FILENAME = "users.db";
	
	private ServerData data;
	private ServerSocket sSocket=null;
	private AcceptClients t_acceptclients;
	
	private boolean isRunning = false;
	RMI_NavalBattle_Service rmiNavalBattle;
	
	
	
	/*------------------------*/
	
	@SuppressWarnings("unused")
	public static void main(String args[]){
		
		Server srv = new Server(SERVICE_PORT);
		ServerGUI gui = new ServerGUI(srv, 750, 530);
				
	}
    
	public Server(int port){
		
		data = new ServerData(this);
		data.setUsers_filename(USERS_FILENAME);
		
		startServer();
				
		return;		
	
	}
	
	/*
	 * Start RMI service
	 * */
	public void StartRMI(){
		
	try {
        // LOAD THE SERVICE
		
		System.out.println("Loading RMI...");
    
    	rmiNavalBattle = new RMI_NavalBattle_Service();

    	RemoteRef location = rmiNavalBattle.getRef();
    
 
        Registry r1;
        
        r1=LocateRegistry.createRegistry(Registry.REGISTRY_PORT);
        r1.rebind("rmiNavalBattle", rmiNavalBattle);
        
    	
		}catch (RemoteException e){
        	System.err.println ("Remote Error - " + e);
    		}catch (Exception e){
    		System.err.println ("Error - " + e);
      	}
        
}
	//cancell RMI Service
	public void disconnectRMI() {  
	    
		try {
			rmiNavalBattle.notifyDisconnect();
		} catch (RemoteException e1) {
			e1.printStackTrace();
		}
		
		try {  
	        Naming.unbind("rmiNavalBattle");
	        
	    }  
	    catch (Exception e) {  
	        System.out.println("Failed To unbind object with the RMI registry");  
	    }  
	}

	
	/*------- GETTERS AND SETTERS ---------*/
	
	public RMI_NavalBattle_Service getRmiNavalBattle() {
		return rmiNavalBattle;
	}
		
	public boolean isRunning(){
		return isRunning;
	}
	
	public ServerData getData(){
		return data;
	}
	
	
	/*----- PRIVATE FUNCTIONS ----*/
	
	/**  
	 * Open new Server Socket to accept clients
	 */
    private boolean openSocket(){
		    
		try {
			sSocket = new ServerSocket(SERVICE_PORT);
			sSocket.setSoTimeout(TIMEOUT);
			
		}catch (BindException bexc){ 
	           // System.err.println("Service already running on port " + Server.SERVICE_PORT); 
	            data.log("Service already running on port " + Server.SERVICE_PORT, Log.ERROR);
	            return false;
		} catch (IOException e) {
			//System.err.println("Error binding ServerSocket!");
			data.log("Error binding ServerSocket!" + Server.SERVICE_PORT, Log.ERROR);
			return false;
		} 
		
		return true;
		
	}

    /**  
	 * Closes que Server Socket, catching any Exception
	 */
	private void closeSocket(){
		try {
			sSocket.close();
		} catch (IOException e) {
			data.log("Error closing socket!", Log.ERROR);
		}
	}
	
	
	/*------- PUBLIC FUNCTIONS --------*/
	
	
	/** 
	 * Disconnect clients and shutdown the server. 
	 */
	public void shutdown(){
			
		if (!isRunning)
			return;
		
		disconnectRMI();
		
		//stop thread that accepts new clients
		t_acceptclients.requestStop();
		
		try {
			t_acceptclients.join(TIMEOUT*2);
		} catch (InterruptedException e) {
			System.out.println("Critical Error: " + e);
		}
		
		//data.terminateGames();
		data.disconnectAll();
		data.log("Server Shutted Down!", Log.IMPORTANT_INFO);
		
		closeSocket();
		
		isRunning = false;
		
		setChanged();
		notifyObservers();
		
	}

	
	
	/**  
	 * Starts the Server.
	 */
	public void startServer(){
		
		if (isRunning)
			return;
		
		data.log("Starting server...", Log.IMPORTANT_INFO);
		
		StartRMI();
		
		openSocket();
		
        //System.out.println("Server ["+sSocket.getInetAddress().getHostAddress()+"] Running on Port: "+sSocket.getLocalPort()+"!");
		
		data.loadUsers();
		
		t_acceptclients = new AcceptClients(this, sSocket);
		t_acceptclients.start();
		System.out.println("Accepting Clients...");
		
		isRunning = true;
		
		data.log("Server Running!", Log.IMPORTANT_INFO);
		
		setChanged();
		notifyObservers();
		
	}
	
	
	/** 
	 * Request Observers/GUI update
	 */
	public void requestUpdate(){
		setChanged();
		notifyObservers();		
	}
	
	
}
